package com.huawei.cloudapp.utils;

import static com.huawei.cloudapp.utils.CasConstantsUtil.ENCRYPT_IV;
import static com.huawei.cloudapp.utils.CasConstantsUtil.PASSWORD;

import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.util.Base64;
import android.util.Log;

import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;

public class CasAESKeystoreUtils {
    private static String TAG = "CasAESKeystoreUtils";
    private static String TRANSFORMATION = "AES/GCM/NoPadding";
    private static final String ALIAS = "keyAlias";

    private static void createKey() {
        final KeyGenerator keyGenerator;
        AlgorithmParameterSpec spec = null;
        try {
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
                keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
                Calendar start = new GregorianCalendar();
                Calendar end = new GregorianCalendar();
                end.add(Calendar.YEAR, 10);
                spec = new KeyGenParameterSpec.Builder(ALIAS,
                        KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                        .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                        .setCertificateNotBefore(start.getTime())
                        .setCertificateNotAfter(end.getTime())
                        .build();
                keyGenerator.init(spec);
                keyGenerator.generateKey();
            }
        } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
            Log.e(TAG, "encryptData: ", e);
        }
    }

    public static HashMap<String, String> encryptData(String data) {
        if (!hasAliasKey()) {
            createKey();
        }
        try {
            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyStore.load(null);
            KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore.getEntry(ALIAS, null);
            SecretKey secretKey = secretKeyEntry.getSecretKey();
            //KeyGenParameterSpecs中设置的block模式是KeyProperties.BLOCK_MODE_GCM,所以这里只能使用这个模式解密数据。
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            HashMap<String, String> encryptResult = new HashMap<>();
            encryptResult.put(PASSWORD, Base64.encodeToString(cipher.doFinal(data.getBytes()), Base64.NO_WRAP));
            encryptResult.put(ENCRYPT_IV, Base64.encodeToString(cipher.getIV(), Base64.NO_WRAP));
            return encryptResult;
        } catch (KeyStoreException | IOException | NoSuchAlgorithmException |
                CertificateException | InvalidKeyException | UnrecoverableEntryException |
                NoSuchPaddingException | BadPaddingException |
                IllegalBlockSizeException | NullPointerException e) {
            Log.e(TAG, "encryptData: ", e);
        }
        return null;
    }

    public static String decryptData(String ciphertext, String encryptIv) {
        if (!hasAliasKey()) {
            createKey();
        }
        try {
            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyStore.load(null);
            KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore.getEntry(ALIAS, null);
            SecretKey secretKey = secretKeyEntry.getSecretKey();
            //KeyGenParameterSpecs中设置的block模式是KeyProperties.BLOCK_MODE_GCM,所以这里只能使用这个模式解密数据。
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, Base64.decode(encryptIv, Base64.NO_WRAP));
            cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmParameterSpec);
            return new String(cipher.doFinal(Base64.decode(ciphertext, Base64.NO_WRAP)));
        } catch (InvalidKeyException | InvalidAlgorithmParameterException | IOException |
                CertificateException | NoSuchAlgorithmException | UnrecoverableEntryException |
                NoSuchPaddingException | KeyStoreException |
                BadPaddingException | IllegalBlockSizeException e) {
            Log.e(TAG, "decryptData: ", e);
        }
        return "";
    }

    private static boolean hasAliasKey() {
        try {
            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyStore.load(null);
            KeyStore.Entry keyEntry = keyStore.getEntry(ALIAS, null);
            if (null != keyEntry) {
                return true;
            }
        } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | UnrecoverableEntryException e) {
            Log.e(TAG, "hasAliasKey: ", e);
        }
        return false;
    }
}
